#!/usr/bin/env python
#coding=utf-8

import random
import rospy
import socket
import time
import commands
from paho.mqtt import client as paho_mqtt_client
import threading

from tool.log_parser import Log_Parser
log = Log_Parser()

class Mqtt_Client:

    def __init__(self, node_name):
        self.__node_name = node_name
        self.__host = self.GetLocalIP()

        self.__connected = True                    # MQTT客户端连接状态,默认连接成功
        self.__index = 0
        # generate client ID with pub prefix randomly
        self.__client_id = "mqtt-client-{}".format(random.randint(0, 1000))
        self.__client = paho_mqtt_client.Client(self.__client_id)

        # self.__client.loop_start()
        self.lock = threading.Lock()

    # def __del__(self):
    #     self.CloseClient()

    def Connected(self):    
        '''
        MQTT客户端连接状态
        '''             
        return self.__connected

    def GetLocalIP(self):
        '''
        获取本机ip地址
        '''
        return "127.0.0.1"
    
    def GetCurrentClient_ID(self):
        '''
        获取当前客户端ID
        '''
        return self.__client_id

    def on_connect(self, client, userdata, flags, rc):
        """
        _summary_
        连接成功回调
        Args:
            client (_type_): _description_
            userdata (_type_): _description_
            flags (_type_): _description_
            rc (_type_): _description_
        Returns:
            _type_: _description_
        """
        # rospy.logwarn("on_connect MQTT rc = [{}].".format(rc))

        if rc == 0:
            # 连接成功
            self.__connected = True

            logmsg = ("[{}][on_connect] MQTT rc = [{}].".format(self.__node_name,rc))
            print(logmsg)
            log.LogInfo(self.__node_name,logmsg)


    def on_disconnect(self, client, userdata, rc):
        '''
        断联有两种情况
        正常断开连接MQTT_ERR_SUCCESS (0)
        网络不正常断开连接 rc != 0
        '''
        '''
        rc = 0  0x00 成功 包含这些状态：CONNACK, PUBACK, PUBREC, PUBREL, PUBCOMP, UNSUBACK, AUTH

        rc = 16  0x10 包含这些状态：PUBACK, PUBREC

        '''
        # rospy.logwarn("on_disconnect MQTT rc = [{}].".format(rc))
        if rc != 0 or  rc != 16 :
            self.__connected = False
            logmsg = ("[{}][on_disconnect] MQTT rc = [{}],client_id:[{}].self.__connected = {}".format(self.__node_name,rc,self.__client_id,self.__connected))
            print(logmsg)
            log.LogInfo(self.__node_name,logmsg)
     
        if rc == 16:
        # 记载状态
            logmsg = ("[{}][on_disconnect] MQTT rc = [{}],client_id:[{}].self.__connected = {}".format(self.__node_name,rc,self.__client_id,self.__connected))
            print(logmsg)
            log.LogInfo(self.__node_name,logmsg)


            
    def on_connect_fail(self, client, userdata):
        """
        _summary_
        连接失败
        Args:
            client (_type_): _description_
            userdata (_type_): _description_
        """
        # rospy.logwarn("on_connect_fail MQTT ")

        self.__connected = False
        logmsg = ("[{}] on_connect_fail MQTT .self.__connected = {}".format(self.__node_name,self.__connected))
        log.LogInfo(self.__node_name,logmsg)



    def ConnectMqttServer(self):
        '''
        mqtt_client连接操作
        '''
        # rospy.loginfo("[{}][ConnectMqttServer]Connecting to MQTT Broker...".format(self.__node_name))
        logmsg = ("[{}][ConnectMqttServer]Connecting to MQTT Broker...".format(self.__node_name))
        log.LogInfo(self.__node_name,logmsg)
        try:
            self.__client.on_connect = self.on_connect
            self.__client.on_connect_fail = self.on_connect_fail
            self.__client.on_disconnect = self.on_disconnect
            self.__client.connect(self.__host, "1883", 1)
            self.__client.loop_start()
            # self.__connected = True(以 on_connect为主)
        except Exception as err:
            self.__connected = False
            # rospy.loginfo("[{}][ConnectMqttServer]Connect to MQTT Broker fail.{}.".format(self.__node_name,err))
            logmsg = ("[{}][ConnectMqttServer]Connect to MQTT Broker fail.{}.self.__connected = {}".format(self.__node_name,err,self.__connected))
            log.LogInfo(self.__node_name,logmsg)
            


    def CloseClient(self):
        '''
        mqtt_client关闭操作
        '''
        # print("------------------CloseClient------------------")
        self.__connected = False

        self.__client.disconnect()
        
        # loop_stop() 不能写在on_disconnect 回调里, 否则 threading.current_thread() == client._thread，\
        # 客户端无法清除client._thread 子进程，以后再使用loop_start()就无效了
        self.__client.loop_stop()
        
        # print("[{}][CloseClient]Disonnected to MQTT Broker.".format(self.__node_name))
        logmsg = "[{}][CloseClient]Close client to MQTT Broker. self.__connected = {}".format(self.__node_name,self.__connected)
        log.LogInfo(self.__node_name,logmsg)



    def ReconnectMqttServer(self):
        '''
        mqtt 重联操作
        '''
        try:
            self.CloseClient()

            time.sleep(1)

            self.__client.reconnect() # 必须重连将 client._state 从断开状态切换为初始化状态
            self.__client.loop_start()

            logmsg = ("[{}][ReconnectMqttServer]Client id : [{}].Successfully reconnect to MQTT Broker...".format(self.__node_name,str(self.GetCurrentClient_ID())))
            print(logmsg)
            log.LogInfo(self.__node_name,logmsg)
        except Exception as err:
            logmsg = ("[{}][ReconnectMqttServer]Client id : [{}] .Flied reconnec to MQTT Broker err: {}".format(self.__node_name,str(self.GetCurrentClient_ID()),str(err)))
            log.LogInfo(self.__node_name,logmsg)





    def KickCurrentMqttClient(self,current_client_id):
        '''
        关闭当前客户端
        '''
        try:
            kick_status = False # 默认关闭失败
            self.CloseClient()

            command = 'emqx ctl clients kick {}'.format(str(current_client_id))            # 这个命令在root下 #emqx ctl clients kick mqtt-client-221
            status,output = commands.getstatusoutput(command)
            if status != 0:
                raise Exception("emqx ctl clients kick {} error:{}".format(str(current_client_id),output))
           
            logmsg = ("[{}][KickCurrentMqttClient] emqx ctl clients kick {} result : {}".format(self.__node_name,str(current_client_id),output))  # 记载状态
            print(logmsg)
            log.LogInfo(self.__node_name,logmsg)

            '''
            emqx ctl clients kick mqtt-client-221

            ok
            '''

            if "ok" in output :
                # Node 'emqx@127.0.0.1' 5.0.8 is started # 服务器正常，命令返回值
                kick_status = True
                return kick_status
            else:
                kick_status = False
                return kick_status

        except Exception as error:
            kick_status = False
            logmsg = ("[{}][GetEmqxStatus] [KickCurrentMqttClient] emqx ctl clients kick {} error : {}".format(self.__node_name,str(current_client_id),str(error)))  # 记载状态
            log.LogInfo(self.__node_name,logmsg)

            return kick_status

    def CreateNewMqttClient(self):
        '''
        创建新的客户端，连接
        '''
        try:
            self.__client_id = "mqtt-client-{}".format(random.randint(0, 1000))
            self.__client = paho_mqtt_client.Client(self.__client_id)

            logmsg = ("[{}][CreateNewMqttClient] New client id :[{}] , connecting to MQTT Broker...".format(self.__node_name, self.__client_id))
            print(logmsg)
            log.LogInfo(self.__node_name,logmsg)

            self.__client.on_connect = self.on_connect
            self.__client.on_connect_fail = self.on_connect_fail
            self.__client.on_disconnect = self.on_disconnect
            self.__client.connect(self.__host, "1883", 1)
            self.__client.loop_start()
            # self.__connected = True(以 on_connect为主)
        except Exception as err:
            self.__connected = False
            logmsg = ("[{}][CreateNewMqttClient]New client id :[{}] , Connect to MQTT Broker fail.{}.self.__connected = {}".format(self.__node_name,self.__client_id,err,self.__connected))
            log.LogInfo(self.__node_name,logmsg)




    def GetEmqxStatus(self):
        '''
        获取服务器状态
        '''
        try:
            emqx_status = True # 默认连接

            command = 'emqx ctl status'            # 这个命令在root下
            status,output = commands.getstatusoutput(command)
            print("emqx ctl status :{}".format(status))
            print("emqx ctl output :{}".format(output))

            if status != 0:
                raise Exception("emqx ctrl status command error : {}".format(output))
            
            logmsg = ("[{}][GetEmqxStatus] emqx  status : {}".format(self.__node_name,output))  # 记载状态
            log.LogInfo(self.__node_name,logmsg)

            if "started" in output:
                # Node 'emqx@127.0.0.1' 5.0.8 is started # 服务器正常，命令返回值
                emqx_status = True
                return emqx_status
            else:
                emqx_status = False
                return emqx_status

        except Exception as error:
            emqx_status = False
            logmsg = ("[{}][GetEmqxStatus] emqx  status : {}".format(self.__node_name,str(error)))  # 记载状态
            log.LogInfo(self.__node_name,logmsg)

            return emqx_status



    def ReconnectEmqxServer(self):
        '''
        重启emqx服务器
        '''
        try:
            emqx_status = False # 默认重启失败

            command = 'emqx start'            # 这个命令在root下
            status,output = commands.getstatusoutput(command)
            if status != 0:
                raise Exception("emqx ctrl status command error:{}".format(output))
            print(output)
            logmsg = ("[{}][ReconnectEmqxServer] reboot emqx  status : {}".format(self.__node_name,output))  # 记载状态
            log.LogInfo(self.__node_name,logmsg)

            '''
            sudo emqx start

            !!!!!!
            WARNING: Default (insecure) Erlang cookie is in use.
            WARNING: Configure node.cookie in /etc/emqx/emqx.conf or override from environment variable EMQX_NODE__COOKIE
            NOTE: Use the same config value for all nodes in the cluster.
            !!!!!!
            EMQX 5.0.8 is started successfully!

            !!!!!!
            WARNING: Default (insecure) Erlang cookie is in use.
            WARNING: Configure node.cookie in /etc/emqx/emqx.conf or override from environment variable EMQX_NODE__COOKIE
            NOTE: Use the same config value for all nodes in the cluster.
            !!!!!!
            ERROR: node_is_already_running!

            '''

            if "node_is_already_running" in output or "successfully" in output:
                # Node 'emqx@127.0.0.1' 5.0.8 is started # 服务器正常，命令返回值
                emqx_status = True
                return emqx_status
            else:
                emqx_status = False
                return emqx_status

        except Exception as error:
            emqx_status = False
            logmsg = ("[{}][GetEmqxStatus] reboot emqx  status : {}".format(self.__node_name,str(error)))  # 记载状态
            log.LogInfo(self.__node_name,logmsg)

            return emqx_status
        





    def ConvertTime(self, secs):
        '''
            转换时间到字符串
        '''
        return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(secs))

    def ConvertBool(self, bool):
        '''
        转换布尔量到数值
        '''
        return 1 if bool else 0

    def dataAppendPlus(self, msg):
        """
        发送的索引自增
        _summary_
        """
        self.__index += 1
        if (self.__index > 10000000):
            self.__index = 0
        msg["index"] = self.__index



    def PublishTopic(self, topic, msg, qos=0, retain=False):
        '''
        mqtt_client发布话题
        '''
        if self.__connected:
            # 先要获取锁
            self.lock.acquire()
            try:
                # 修改
                topic_ = "/out/{}".format(topic)
                result = self.__client.publish(topic_, msg, qos, retain)
                # print(result.rc) # 成功发送，返回值 result.rc = 0
            finally:
                # 改完了一定要释放锁
                self.lock.release()
                status = result[0]
                if status != 0: # 发送失败
                    self.__connected = False
                    # rospy.logerr("[{}]mqtt Failed to send message to topic {}".format(self.__node_name, topic))
                    logmsg = ("[{}]mqtt Failed to send message to topic {}，self.__connected = {}".format(self.__node_name, topic,self.__connected))
                    log.LogInfo(self.__node_name,logmsg)
                    
                else:
                    # rospy.loginfo(
                    #     "[{}]mqtt Success to send message to topic {}".format(
                    #         self.__node_name, topic))
                    pass
                return status
